//
// Created by luoen on 19-7-16.
//

#include <malloc.h>
#include "linked_list.h"

linkedList* _create_linked_list(char type) {
    linkedList* list = NULL;
    list = malloc(sizeof(*list));
    if (NULL == list) {
        return NULL;
    }
    if (type < 0 || type > 2) {
        list->type = 0;
    } else {
        list->type = type;
    }
    list->head = NULL;
    list->tail = NULL;
    list->free = NULL;
    list->length = 0;
    return list;
}

linkedList* _insert_node(linkedList* list, listNode* node, size_t index) {
    if (NULL == list || NULL == node) {
        return list;
    }
    if (index > list->length) {
        return list;
    }
    node->pre = NULL;
    node->next = NULL;
    if (index == list->length) {
        if (NULL == list->head && list->length == 0) {
            list->head = node;
            list->tail = node;
        } else {
            listNode* temp = list->head;
            listNode* last = list->head;
            while (NULL != last) {
                temp = last;
                last = last->next;
            }
            temp->next = node;
            list->tail = node;
            switch (list->type) {
                case 1:
                    node->pre = temp;
                    break;
                case 2:
                    node->pre = temp;
                    node->next = list->head;
                    break;
                default:
                    break;
            }
        }
    } else if (index == 0) {
        listNode* temp = list->head;
        list->head = node;
        node->next = temp;
        switch (list->type) {
            case 1:
                temp->pre = node;
                break;
            case 2:
                temp->pre = node;
                node->pre = list->tail;
                break;
            default:
                break;
        }
    } else {
        listNode* temp = list->head;
        while (index > 1) {
            temp = temp->next;
            --index;
        }
        listNode* after = temp->next;
        temp->next = node;
        node->next = after;
        if (list->type != 0) {
            node->pre = temp;
            after->pre = node;
        }
    }

    ++list->length;
    return list;
}

linkedList* _insert_data(linkedList* list, void * data, size_t index) {
    if (NULL == list || NULL == data) {
        return list;
    }
    listNode* node = NULL;
    node = malloc(sizeof(*node));
    if (NULL == node) {
        return list;
    }
    node->data = data;
    return _insert_node(list, node, index);
}

linkedList* _insert_node_to_head(linkedList* list, listNode* node) {
    return _insert_node(list, node, 0);
}

linkedList* _insert_data_to_head(linkedList* list, void * data) {
    return _insert_data(list, data, 0);
}

linkedList* _insert_node_to_tail(linkedList* list, listNode* node) {
    if (NULL == list) {
        return NULL;
    }
    return _insert_node(list, node, list->length);
}

linkedList* _insert_data_to_tail(linkedList* list, void * data) {
    if (NULL == list) {
        return NULL;
    }
    return _insert_data(list, data, list->length);
}

linkedList* _delete_data(linkedList* list, void* data) {
    return list;
}

linkedList* _delete_node(linkedList* list, listNode* node) {
    return list;
}

linkedList* _reverse_linked_list(linkedList* list) {
    if (NULL == list) {
        return NULL;
    }
    if (list->length == 0 || list->length == 1) {
        return list;
    }
    if (list->type != 0) {
        listNode* ori_tail = list->tail;
        listNode* ori_head = list->head;
        list->tail = ori_head;
        while (ori_head != ori_tail) {
            listNode* pre = ori_head->pre;
            listNode* next = ori_head->next;
            ori_head->pre = next;
            ori_head->next = pre;
            ori_head = next;
        }
        list->head = ori_tail;
        listNode* pre2 = ori_tail->pre;
        listNode* next2 = ori_tail->next;
        list->head->pre = next2;
        list->head->next = pre2;
    } else {
        listNode* pre = NULL;
        listNode* next = NULL;
        listNode* cur = list->head;
        list->tail = cur;
        while (NULL != cur) {
            next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = next;
        }
        list->head = pre;
    }
    return list;
}

listNode* _find_node_by_index(linkedList* list, size_t index) {

}

listNode* _find_node_by_data(linkedList* list, void* data) {

}

void _empty_linked_list(linkedList* list) {
    if (NULL == list) {
        return;
    }

    while (NULL != list->head) {
        listNode* tNode = list->head;
        list->head = tNode->next;
        if (NULL != tNode) {
            if (NULL != tNode->data) {
                if (NULL != list->free) {
                    list->free(tNode->data);
                } else {
                    free(tNode->data);
                }
            }
            tNode->next = NULL;
            free(tNode);
        }
        tNode = NULL;
    }
}

void _release_linked_list(linkedList* list) {
    if (NULL == list) {
        return;
    }

    _empty_linked_list(list);

    free(list);

    list = NULL;
}

size_t _linked_list_length(linkedList* list) {
    return NULL == list ? 0 : list->length;
}